﻿namespace OasisCiq.Xml
{
    // ReSharper disable MemberCanBePrivate.Global

    using System.Linq;
    using System.Xml;
    using Xal;

    public class XalXmlWriter
    {
        private const string XmlNS = XmlNamespaces.Address;
        private readonly CommonTypesXmlWriter ctWriter;
        private readonly XLinkXmlWriter xLinkWriter;
        private readonly XmlWriter writer;

        public XalXmlWriter(XmlWriter writer)
        {
            this.writer = writer;
            ctWriter = new CommonTypesXmlWriter(writer);
            xLinkWriter = new XLinkXmlWriter(writer);
        }

        public void WriteAddress(AddressType element)
        {
            WriteAddress(element, "Address", XmlNS);
        }

        public void WriteAddress(AddressType element, string elementName, string elementNamespace)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement(elementName, elementNamespace);
            WriteFreeTextAddress(element.FreeTextAddress);
            WriteCountry(element.Country);
            WriteAdministrativeArea(element.AdministrativeArea);
            WriteLocality(element.Locality);
            WriteThoroughfare(element.Thoroughfare);
            WritePremises(element.Premises);
            WritePostCode(element.PostCode);
            WriteRuralDelivery(element.RuralDelivery);
            WritePostalDeliveryPoint(element.PostalDeliveryPoint);
            WritePostOffice(element.PostOffice);
            WriteLocationByCoordinates(element.LocationByCoordinates);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.AddressId != null)
            {
                writer.WriteAttributeString("AddressID", XmlNS, element.AddressId.ToString());
            }

            if (element.AddressIdType != null)
            {
                writer.WriteAttributeString("AddressIDType", XmlNS, element.AddressIdType.ToString());
            }

            if (element.Id != null)
            {
                writer.WriteAttributeString("ID", XmlNS, element.Id.ToString());
            }

            if (element.Usage != null)
            {
                writer.WriteAttributeString("Usage", XmlNS, element.Usage.ToString());
            }

            if (element.DeliveryMode != null)
            {
                writer.WriteAttributeString("DeliveryMode", XmlNS, element.DeliveryMode.ToString());
            }

            ctWriter.WriteStatusList(element.Status, XmlNS);
            ctWriter.WriteValidityDateGroup(element);
            if (element.AddressKey != null)
            {
                writer.WriteAttributeString("AddressKey", XmlNS, element.AddressKey.ToString());
            }

            if (element.AddressKeyRef != null)
            {
                writer.WriteAttributeString("AddressKeyRef", XmlNS, element.AddressKeyRef.ToString());
            }

            xLinkWriter.WriteXLink(element);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteLanguageCodeGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteEndElement();
        }

        public void WriteFreeTextAddress(FreeTextAddress element)
        {
            if (element == null || element.AddressLines == null || 
                element.AddressLines.Count < 1) // at least one line required
            {
                return;
            }

            writer.WriteStartElement("FreeTextAddress", XmlNS);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var addressLine in element.AddressLines)
            {
                WriteAddressLine(addressLine);
            }

            writer.WriteEndElement();
        }

        public void WriteCountry(CountryType element)
        {
            WriteCountryStartElement(element, "Country", XmlNS);
            WriteCountryEndElement(element);
        }

        /// <summary>
        /// Write start element with attributes
        /// </summary>
        /// <param name="element"></param>
        /// <param name="elementName"></param>
        /// <param name="elementNamespace"></param>
        public void WriteCountryStartElement(CountryType element, string elementName, string elementNamespace)
        {
            if (element == null || element.NameElements == null || 
                element.NameElements.Count < 1) // at least one name required
            {
                return;
            }

            writer.WriteStartElement(elementName, elementNamespace);
            ctWriter.WriteExtendedAttributes(element);
        }

        /// <summary>
        /// Write children elements and close root tag
        /// </summary>
        public void WriteCountryEndElement(CountryType element)
        {
            if (element == null || element.NameElements == null ||
                element.NameElements.Count < 1) // at least one name required
            {
                return;
            }

            foreach (var countryNameElement in element.NameElements)
            {
                WriteCountryNameElement(countryNameElement);
            }

            writer.WriteEndElement();
        }

        public void WriteAdministrativeArea(AdministrativeArea element)
        {
            if (element == null || element.NameElements == null ||
                element.NameElements.Count < 1) // at least one name required
            {
                return;
            }

            writer.WriteStartElement("AdministrativeArea", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);

            foreach (var administrativeAreaNameElement in element.NameElements)
            {
                WriteAdministrativeAreaNameElement(administrativeAreaNameElement);
            }

            if (element.SubAdministrativeArea != null)
            {
                WriteSubAdministrativeArea(element.SubAdministrativeArea);
            }

            writer.WriteEndElement();
        }

        public void WriteLocality(Locality element)
        {
            if (element == null || element.NameElements == null ||
                element.NameElements.Count < 1) // at least one name required
            {
                return;
            }

            writer.WriteStartElement("Locality", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);

            foreach (var localityNameElement in element.NameElements)
            {
                WriteLocalityNameElement(localityNameElement);
            }

            if (element.SubLocality != null)
            {
                WriteSubLocality(element.SubLocality);
            }

            writer.WriteEndElement();
        }

        public void WriteThoroughfare(Thoroughfare element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Thoroughfare", XmlNS);
            WriteThoroughfareTypeWithoutElement(element);
            if (element.SubThoroughfares != null)
            {
                foreach (var thoroughfareType in element.SubThoroughfares.Take(5))
                {
                    writer.WriteStartElement("SubThoroughfare", XmlNS);
                    WriteThoroughfareTypeWithoutElement(thoroughfareType);
                    writer.WriteEndElement();
                }
            }

            writer.WriteEndElement();
        }

        public void WritePremises(Premises element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("SubPremises", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.TypeCode != null)
            {
                writer.WriteAttributeString("TypeCode", XmlNS, element.TypeCode.ToString());
            }

            WritePremisesTypeWithoutElement(element);
            if (element.SubPremises != null)
            {
                foreach (var subPremises in element.SubPremises)
                {
                    WriteSubPremises(subPremises);
                }
            }

            writer.WriteEndElement();
        }

        public void WritePostCode(PostCode element)
        {
            if (element == null || element.Identifiers == null ||
                element.Identifiers.Count < 1) // at least one identifier required
            {
                return;
            }

            writer.WriteStartElement("PostCode", XmlNS);
            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var id in element.Identifiers)
            {
                WriteIdentifierType(id, "Identifier");
            }

            writer.WriteEndElement();
        }

        public void WriteRuralDelivery(RuralDelivery element)
        {
            if (element == null || element.Identifiers == null ||
                element.Identifiers.Count < 1) // at least one identifier required
            {
                return;
            }

            writer.WriteStartElement("RuralDelivery", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var id in element.Identifiers)
            {
                WriteIdentifierType(id, "Identifier");
            }

            writer.WriteEndElement();
        }

        public void WritePostalDeliveryPoint(PostalDeliveryPoint element)
        {
            if (element == null || element.Identifiers == null ||
                element.Identifiers.Count < 1) // at least one identifier required
            {
                return;
            }

            writer.WriteStartElement("PostalDeliveryPoint", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var id in element.Identifiers)
            {
                WriteIdentifierType(id, "Identifier");
            }

            writer.WriteEndElement();
        }

        public void WritePostOffice(PostOffice element)
        {
            if (element == null || element.Identifiers == null ||
                element.Identifiers.Count < 1) // at least one identifier required
            {
                return;
            }

            writer.WriteStartElement("PostOffice", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var id in element.Identifiers)
            {
                WriteIdentifierType(id, "Identifier");
            }

            writer.WriteEndElement();
        }

        // TODO: GeoRSS serialization

        public void WriteLocationByCoordinates(LocationByCoordinates element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("LocationByCoordinates", XmlNS);
            if (element.Meridian != null)
            {
                writer.WriteAttributeString("Meridian", XmlNS, element.Meridian.ToString());
            }

            if (element.MeridianCodeType != null)
            {
                writer.WriteAttributeString("MeridianCodeType", XmlNS, element.MeridianCodeType.ToString());
            }

            if (element.Datum != null)
            {
                writer.WriteAttributeString("Datum", XmlNS, element.Datum.ToString());
            }

            if (element.DatumCodeType != null)
            {
                writer.WriteAttributeString("DatumCodeType", XmlNS, element.DatumCodeType.ToString());
            }

            if (element.Projection != null)
            {
                writer.WriteAttributeString("Projection", XmlNS, element.Projection.ToString());
            }

            if (element.ProjectionCodeType != null)
            {
                writer.WriteAttributeString("ProjectionCodeType", XmlNS, element.ProjectionCodeType.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            WriteLatitude(element.Latitude);
            WriteLongitude(element.Longitude);
            writer.WriteEndElement();
        }

        public void WriteAddressLine(AddressLine element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("AddressLine", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WriteCountryNameElement(CountryNameElement element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("NameElement", XmlNS);
            ctWriter.WriteAbbreviationGroup(element);
            if (element.NameType != null)
            {
                writer.WriteAttributeString("NameType", XmlNS, element.NameType.ToString());
            }

            if (element.NameCode != null)
            {
                writer.WriteAttributeString("NameCode", XmlNS, element.NameCode.ToString());
            }

            if (element.NameCodeType != null)
            {
                writer.WriteAttributeString("NameCodeType", XmlNS, element.NameCodeType.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WriteAdministrativeAreaNameElement(AdministrativeAreaNameElement element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("NameElement", XmlNS);
            ctWriter.WriteAbbreviationGroup(element);
            if (element.NameType != null)
            {
                writer.WriteAttributeString("NameType", XmlNS, element.NameType.ToString());
            }

            if (element.NameCode != null)
            {
                writer.WriteAttributeString("NameCode", XmlNS, element.NameCode.ToString());
            }

            if (element.NameCodeType != null)
            {
                writer.WriteAttributeString("NameCodeType", XmlNS, element.NameCodeType.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WriteSubAdministrativeArea(SubAdministrativeArea element)
        {
            if (element == null || element.NameElements == null ||
                element.NameElements.Count < 1) // at least one name required
            {
                return;
            }

            writer.WriteStartElement("SubAdministrativeArea", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var subAdministrativeAreaNameElement in element.NameElements)
            {
                WriteSubAdministrativeAreaNameElement(subAdministrativeAreaNameElement);
            }

            writer.WriteEndElement();
        }

        public void WriteSubAdministrativeAreaNameElement(SubAdministrativeAreaNameElement element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("NameElement", XmlNS);
            ctWriter.WriteAbbreviationGroup(element);
            if (element.NameType != null)
            {
                writer.WriteAttributeString("NameType", XmlNS, element.NameType.ToString());
            }

            if (element.NameCode != null)
            {
                writer.WriteAttributeString("NameCode", XmlNS, element.NameCode.ToString());
            }

            if (element.NameCodeType != null)
            {
                writer.WriteAttributeString("NameCodeType", XmlNS, element.NameCodeType.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WriteLocalityNameElement(LocalityNameElement element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("NameElement", XmlNS);
            ctWriter.WriteAbbreviationGroup(element);
            if (element.NameType != null)
            {
                writer.WriteAttributeString("NameType", XmlNS, element.NameType.ToString());
            }

            if (element.NameCode != null)
            {
                writer.WriteAttributeString("NameCode", XmlNS, element.NameCode.ToString());
            }

            if (element.NameCodeType != null)
            {
                writer.WriteAttributeString("NameCodeType", XmlNS, element.NameCodeType.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WriteSubLocality(SubLocality element)
        {
            if (element == null || element.NameElements == null ||
                element.NameElements.Count < 1) // at least one name required
            {
                return;
            }

            writer.WriteStartElement("SubLocality", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);
            foreach (var subLocalityNameElement in element.NameElements)
            {
                WriteSubLocalityNameElement(subLocalityNameElement);
            }

            writer.WriteEndElement();
        }

        public void WriteSubLocalityNameElement(SubLocalityNameElement element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("NameElement", XmlNS);
            ctWriter.WriteAbbreviationGroup(element);
            if (element.NameType != null)
            {
                writer.WriteAttributeString("NameType", XmlNS, element.NameType.ToString());
            }

            if (element.NameCode != null)
            {
                writer.WriteAttributeString("NameCode", XmlNS, element.NameCode.ToString());
            }

            if (element.NameCodeType != null)
            {
                writer.WriteAttributeString("NameCodeType", XmlNS, element.NameCodeType.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WriteThoroughfareTypeWithoutElement(ThoroughfareType element)
        {
            if (element == null)
            {
                return;
            }

            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.TypeCode != null)
            {
                writer.WriteAttributeString("TypeCode", XmlNS, element.TypeCode.ToString());
            }

            ctWriter.WriteDataQualityGroup(element);
            ctWriter.WriteExtendedAttributes(element);

            if (element.NameElements != null)
            {
                foreach (var thoroughfareNameElement in element.NameElements)
                {
                    WriteThoroughfareNameElement(thoroughfareNameElement);
                }
            }

            if (element.Numbers != null)
            {
                foreach (var identifierType in element.Numbers)
                {
                    WriteIdentifierType(identifierType, "Number");
                }
            }
        }

        public void WriteThoroughfareNameElement(ThoroughfareNameElement element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("NameElement", XmlNS);
            ctWriter.WriteAbbreviationGroup(element);
            if (element.NameType != null)
            {
                writer.WriteAttributeString("NameType", XmlNS, element.NameType.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WriteIdentifierType(IdentifierType element, string elementName)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement(elementName, XmlNS);
            ctWriter.WriteAbbreviationGroup(element);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WritePremisesTypeWithoutElement(PremisesType element)
        {
            if (element == null)
            {
                return;
            }

            ctWriter.WriteExtendedAttributes(element);

            if (element.NameElements != null)
            {
                foreach (var premisesNameElement in element.NameElements)
                {
                    WritePremisesNameElement(premisesNameElement);
                }
            }

            if (element.Numbers != null)
            {
                foreach (var identifierType in element.Numbers)
                {
                    WriteIdentifierType(identifierType, "Number");
                }
            }
        }

        public void WritePremisesNameElement(PremisesNameElement element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("NameElement", XmlNS);
            ctWriter.WriteAbbreviationGroup(element);
            if (element.NameType != null)
            {
                writer.WriteAttributeString("NameType", XmlNS, element.NameType.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteString(element.ToString());
            writer.WriteEndElement();
        }

        public void WriteSubPremises(SubPremises element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("SubPremises", XmlNS);
            if (element.Type != null)
            {
                writer.WriteAttributeString("Type", XmlNS, element.Type.ToString());
            }

            if (element.TypeCode != null)
            {
                writer.WriteAttributeString("TypeCode", XmlNS, element.TypeCode.ToString());
            }

            WritePremisesTypeWithoutElement(element);
            writer.WriteEndElement();
        }

        public void WriteLatitude(Latitude element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Latitude", XmlNS);
            if (element.DegreesMeasure != null)
            {
                writer.WriteAttributeString("DegreesMeasure", XmlNS, element.DegreesMeasure.ToString());
            }

            if (element.MinutesMeasure != null)
            {
                writer.WriteAttributeString("MinutesMeasure", XmlNS, element.MinutesMeasure.ToString());
            }

            if (element.SecondsMeasure != null)
            {
                writer.WriteAttributeString("SecondsMeasure", XmlNS, element.SecondsMeasure.ToString());
            }

            if (element.Direction != null)
            {
                writer.WriteAttributeString("Direction", XmlNS, element.Direction.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteEndElement();
        }

        public void WriteLongitude(Longitude element)
        {
            if (element == null)
            {
                return;
            }

            writer.WriteStartElement("Longitude", XmlNS);
            if (element.DegreesMeasure != null)
            {
                writer.WriteAttributeString("DegreesMeasure", XmlNS, element.DegreesMeasure.ToString());
            }

            if (element.MinutesMeasure != null)
            {
                writer.WriteAttributeString("MinutesMeasure", XmlNS, element.MinutesMeasure.ToString());
            }

            if (element.SecondsMeasure != null)
            {
                writer.WriteAttributeString("SecondsMeasure", XmlNS, element.SecondsMeasure.ToString());
            }

            if (element.Direction != null)
            {
                writer.WriteAttributeString("Direction", XmlNS, element.Direction.ToString());
            }

            ctWriter.WriteExtendedAttributes(element);
            writer.WriteEndElement();
        }
    }
}